﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.IO;
namespace MDS
{
    public class SFITProxy
    {
       

        public event SFITMessageHandler OnMessageReceived;
        private SFITProxyConfig _config;
        private ManualResetEvent _tcpConnectedEvent = new ManualResetEvent(false);
        private ManualResetEvent _stopEvent = new ManualResetEvent(false);
        TcpClient _tcpClient;
       


        private Thread _connectionThread = null;//重连线程
        private Thread _receiveThread = null;//接收线程
        private ManualResetEvent _tcpDisconnectedEvent = new ManualResetEvent(false);

        private ManualResetEvent _receiveThreadExitEvent = new ManualResetEvent(false);
        private object _startStopLockObj = new object();
        public SFITProxy(SFITProxyConfig config)
        {
            this._config = config;
        }


        public void Start()
        {
            lock (_startStopLockObj)
            {
                if (this.CurrentStatus == Status.Started)
                    return;
                Log.Debug("SFITProxy Starting");
                this.CurrentStatus = Status.Starting;
                _tcpConnectedEvent.Reset();
                _stopEvent.Reset();
                _tcpDisconnectedEvent.Reset();
                _receiveThreadExitEvent.Reset();
                createConnectionThread();
                createRecieveThread();
                this.CurrentStatus = Status.Started;
                Log.Debug("SFITProxy Started");
            }
        }
        public void Stop()
        {
            lock (_startStopLockObj)
            {
                if (this.CurrentStatus == Status.Stopped)
                    return;
                try
                {
                    Log.Debug("SFITProxy Stopping");
                    this.CurrentStatus = Status.Stopping;
                    this._stopEvent.Set();
                    this._tcpConnectedEvent.Reset();
                    this._receiveThreadExitEvent.WaitOne();
                    this._tcpClient.Close();
                }
                catch (Exception ex)
                {

                }
                finally
                {
                    this.CurrentStatus = Status.Stopped;
                    Log.Debug("SFITProxy Stopped");
                }
            }
        }


        #region 连接管理
        private void createConnectionThread()
        {
            this._connectionThread = new Thread(new ThreadStart(connectionProcessor));
            this._connectionThread.Start();
            Thread.Sleep(0);
        }

        private void connectionProcessor()
        {
            while (true)
            {
                TcpClient tcpClient = connectToServer();
                if (tcpClient != null)
                {
                    Log.Debug(string.Format ("SFITProxy 连接成功，IP={0},Port={1}",this._config.IP,this._config.Port));
                    this._tcpClient = tcpClient;
                    this._tcpConnectedEvent.Set();
                    this._tcpDisconnectedEvent.Reset();
                    int index = AutoResetEvent.WaitAny(new WaitHandle[] { this._stopEvent, this._tcpDisconnectedEvent });
                    if (index == 0)
                    {
                        break;
                    }
                }
                else
                {
                    Log.Debug(string.Format("SFITProxy 连接失败，IP={0},Port={1}", this._config.IP, this._config.Port));
                    int index = AutoResetEvent.WaitAny(new WaitHandle[] { this._stopEvent }, 2000);
                    if (index == 0)
                    {
                        break;
                    }
                }

            }
            
        }
        /// <summary>
        /// 连接服务器
        ///     采用异步方式等待连接事件，避免因服务器未开启而耗费过长时间等待
        /// </summary>
        public TcpClient connectToServer()
        {
            int milliSecondsTimeout = 1000;
            TcpClient newTcpClient = new TcpClient();
            newTcpClient.Client.ReceiveBufferSize = 1024 * 1024 * 70;
            IAsyncResult result = null;

            try
            {
                result = newTcpClient.BeginConnect(this._config.IP, this._config.Port, null, null);
                result.AsyncWaitHandle.WaitOne(milliSecondsTimeout, false);

                if (result.IsCompleted)
                {
                    newTcpClient.EndConnect(result);
                    result.AsyncWaitHandle.Close();
                    return newTcpClient;
                }
                else
                {
                    newTcpClient.EndConnect(result);

                    newTcpClient.Close();
                    result.AsyncWaitHandle.Close();
                    return null;
                }
            }
            catch (Exception)
            {
                if (newTcpClient != null) newTcpClient.Close();
                if (result != null) result.AsyncWaitHandle.Close();
                return null;
            }
        }

        #endregion


        #region 接收数据处理
        private void createRecieveThread()
        {

            this._receiveThread = new Thread(new ThreadStart(receiveProcessor));
            this._receiveThread.Start();
            Thread.Sleep(0);

        }
        private void receiveProcessor()
        {

            while (true)
            {
                if (_stopEvent.WaitOne(0))//检查停止事件是否发生
                    break;
                try
                {
                    this._tcpConnectedEvent.WaitOne();
                    byte[] data = socketReceive();
                    SFITRecord record = SFITRecord.Deserialize(data);
                    onRecordReceived(record);
                }
                catch (Exception ex)
                {
                    Log.Debug(string.Format("SFITProxy 连接断开，IP={0},Port={1}", this._config.IP, this._config.Port));
                    this._tcpDisconnectedEvent.Set();
                    this._tcpConnectedEvent.Reset();
                }
            }

            this._receiveThreadExitEvent.Set();
        }
        private byte[] socketReceive()
        {
            while (!this._tcpClient.Client.Poll(1000, SelectMode.SelectRead))
            {
                if (!this._tcpClient.Client.Connected)
                    throw new Exception("TcpClient disconnected");

                continue;
            }
            byte[] buffer = new byte[SFITRecord.SFITRecordLen];

            Int32 nRead = this._tcpClient.Client.Receive(buffer, SFITRecord.SFITRecordLen, 0);
            if (nRead <= 0)
                throw new Exception("TcpClient read failed");


            return buffer;
        }
      
       
        private void onRecordReceived(SFITRecord record)
        {
            try
            {
                SFITMessageHandler handler = this.OnMessageReceived;
                if (handler != null)
                    handler(record);
            }
            catch (Exception ex)
            {
            }

        }

        #endregion

        public Status CurrentStatus { get; private set; }


        public enum Status
        {
            Starting,
            Started,
            Stopping,
            Stopped
        }
    }
}
